package io.github.fantaros.cipher.basement;

import java.util.ArrayList;
import java.util.List;

import io.github.fantaros.cipher.data.WhisperKey;

public class WhisperBlock {
    private final WhisperLogicExecutor whisperLogicExecutor;
    private final byte[] valueArray;
    private final int blockSize;

    public byte get(int offset) throws IllegalArgumentException {
        if (offset < this.blockSize) {
        	return valueArray[offset];
        } else {
        	throw new IllegalArgumentException("block get offset > blockSize");
        }
    }

    public void set(int offset, byte value) throws IllegalArgumentException {
        if (offset < this.blockSize)
            valueArray[offset] = value;
        else
            throw new IllegalArgumentException("block set offset > blockSize");
    }
    
    public WhisperBlock(int blockSize) {
    	this.blockSize = blockSize;
    	this.valueArray = new byte[this.blockSize];
    	whisperLogicExecutor = new WhisperLogicExecutor(blockSize);
    }

    public void refreshData(byte[] input, int offset) {
        for (int i = 0; i < this.blockSize; i++) {
            if (offset + i < input.length) {
            	valueArray[i] = input[offset + i];
            }
            else {
            	valueArray[i] = 0;
            }
        }
    }
    
    public static int unsignedByte(int data) {
		if (data >= 0) {
			return data;
		} else {
			return 256 + data;
		}
	}
    
    public void blockSwapWithKey(WhisperKey key, byte swapper) {
        List<Integer> swapList = key.buildSwapArray(this.blockSize, unsignedByte(swapper));
        byte tmp;
        int location = 0;
        for (Integer o : swapList) {
            int offset = o == null ? -1 : o;
            if (offset >= 0) {
                tmp = this.valueArray[location];
                this.valueArray[location] = this.valueArray[offset];
                this.valueArray[offset] = tmp;
            }
            ++location;
        }
    }
    
    private List<Integer> reverseSwapArray(List<Integer> swapList) {
        if (swapList == null) {
            return null;
        }
        List<Integer> anti = new ArrayList<>(swapList);
        int start = 0, end = swapList.size() - 1;
        while (start < end) {
        	int tmp = anti.get(start);
        	anti.set(start, anti.get(end));
        	anti.set(end, tmp);
        	
        	++start;
        	--end;
        }
        return anti;
    }

    public void deBlockSwapWithKey(WhisperKey key, byte swapper) {
        List<Integer> swapList = this.reverseSwapArray(key.buildSwapArray(this.blockSize, unsignedByte(swapper)));
        byte tmp;
        int location = swapList.size() - 1;
        for (int o = 0; (o < swapList.size() && location >= 0); ++o) {
        	int offset = swapList.get(o) == null ? -1 : swapList.get(o);
        	if (offset >= 0) {
        		tmp = this.valueArray[location];
        		this.valueArray[location] = this.valueArray[offset];
        		this.valueArray[offset] = tmp;
        	}
        	--location;
        }
    }


    public void accept(byte[] output, int offset) {
        for (int i = 0; i < this.blockSize; i++) {
            if (offset + i < output.length) {
                output[offset + i] = valueArray[i];
            }
        }
    }

    public void executeWhisper(int offset, byte keys, byte function) {
        valueArray[offset] = whisperLogicExecutor.logicExecute(valueArray[offset], keys, function);
    }

    public String toString() {
        StringBuilder ret = new StringBuilder();
        for (byte num : valueArray) {
            ret.append(num);
        }
        ret.append("\r\n");
        return ret.toString();
    }
}
